package state


import "fmt"
import ."api"


// 获取长度，不调用元方法
func (self *luaState) RawLen(idx int) uint {

	val := self.stack.get(idx)
	switch x := val.(type) {
	case string: return uint(len(x))
	case *luaTable: return uint(x.len())
	default: return 0
	}
}


// 不需要读取任何栈数据，只要把给定的Lua类型转换成对应的字符串表示
func (self *luaState) TypeName(tp LuaType) string {

	switch tp {
		case LUA_TNONE:		return "no value"

		case LUA_TNIL:		return "nil"

		case LUA_TBOOLEAN:	return "boolean"

		case LUA_TNUMBER:	return "number"

		case LUA_TSTRING:	return "string"

		case LUA_TTABLE:	return "table"

		case LUA_TFUNCTION:	return "function"

		case LUA_TTHREAD:	return "thread"

		default:			return "userdata"
	}
}


// 根据索引返回值的类型，如果索引无效，则返回LUA_TNONE
func (self *luaState) Type(idx int) LuaType {

	if self.stack.isValid(idx) {
		val := self.stack.get(idx)

		return typeOf(val)
	}

	return LUA_TNONE
}


// 判定索引处给定的值是否是无效值
func (self *luaState) IsNone(idx int) bool {

	return self.Type(idx) == LUA_TNONE
}


// 判定索引处给定的值是否是空值
func (self *luaState) IsNil(idx int) bool {

	return self.Type(idx) == LUA_TNIL
}


// 判定索引处给定的值是否是无效值或者空值
func (self *luaState) IsNoneOrNil(idx int) bool {

	return self.Type(idx) <= LUA_TNIL
}


// 判定索引处给定的值是否是布尔值
func (self *luaState) IsBoolean(idx int) bool {

	return self.Type(idx) == LUA_TBOOLEAN
}


// 判定索引处给定的值是否是字符串(或者数字)
func (self *luaState) IsString(idx int) bool {

	t := self.Type(idx)

	return t ==  LUA_TSTRING || t == LUA_TNUMBER
}


// 判定索引处给定的值是否是(或者可以转换为)数字类型
func (self *luaState) IsNumber(idx int) bool {

	_, ok := self.ToNumberX(idx)

	return ok
}


// 判定给定索引处的值是否是整数类型
func (self *luaState) IsInteger(idx int) bool {

	val := self.stack.get(idx)

	_, ok := val.(int64)

	return ok
}


func (self *luaState) IsFunction(idx int) bool {
	return self.Type(idx) == LUA_TFUNCTION
}


// 从指定索引处取出一个布尔值，如果值不是布尔类型，则需要进行类型转换
func (self *luaState) ToBoolean(idx int) bool {

	val := self.stack.get(idx)

	return convertToBoolean(val)
}


// 在Lua里，只有false和nil表示假，其它一切值都表示真
func convertToBoolean(val luaValue) bool {

	switch x := val.(type) {

		case nil:		return false

		case bool:		return x

		default:		return true
	}
}


// ToNumber和ToNumberX()方法从指定索引处取出一个数字
// 如果不是数字类型，则需要进行类型转换
// 这两个方法的区别是:
// 如果值不是数字类型而且也没办法转换成数字类型
// 前者只是简单地返回0
// 后者则会报告转换是否成功

func (self *luaState) ToNumber(idx int) float64 {

	n, _ := self.ToNumberX(idx)

	return n
}

func (self *luaState) ToNumberX(idx int) (float64, bool) {

	val := self.stack.get(idx)

	return convertToFloat(val)
}


// ToInteger() 和 ToIntegerX() 方法从指定索引处取出一个整数值
// 如果值不是整数类型，则需要进行类型转换

func (self *luaState) ToInteger(idx int) int64 {

	i, _ := self.ToIntegerX(idx)

	return i
}

func (self *luaState) ToIntegerX(idx int) (int64, bool) {

	val := self.stack.get(idx)

	return convertToInteger(val)
}


// ToString() 方法从指定索引处取出一个值，如果值是字符串，则返回该字符串
// 如果值是数字，则将值转换为字符串(注意会修改栈)， 然后返回字符串
// 否则，返回空字符串
// 在C API里，该函数只有一个返回值
// 如果返回NULL则表示指引索引处的值不是字符串或者数字
// 由于Go语言字符串类型没有对应的nil值
// 因此参照ToIntegerX() 和 ToNumberX()的做法，增加一个ToStringX()方法
// 其中第二个参数是布尔类型，表示转换是否成功

func (self *luaState) ToString(idx int) string {

	s, _ := self.ToStringX(idx)

	return s
}

func (self *luaState) ToStringX(idx int) (string, bool) {

	val := self.stack.get(idx)

	switch x := val.(type) {

		case string:		return x, true

		case int64, float64:	
			s := fmt.Sprintf("%v", x)	//	以默认的方式打印变量的值
			self.stack.set(idx, s)		// 注意这里会修改栈
			return s, true

		// default:		return "", false
		default:		return fmt.Sprintf("%v", x), false
	}
}


// 判定指定索引处的值是否可以转换成Go函数
func (self *luaState) IsGoFunction(idx int) bool {

	val := self.stack.get(idx)

	if c, ok := val.(*closure); ok {

		return c.goFunc != nil
	}

	return false
}


// 把指定索引处的值转换成Go函数并返回
// 如果值无法转换成Go函数
// 返回nil即可
func (self *luaState) ToGoFunction(idx int) GoFunction {

	val := self.stack.get(idx)

	if c, ok := val.(*closure); ok {

		return c.goFunc
	}

	return nil
}


func (self *luaState) GetAddr(idx int) string {

	val := self.stack.get(idx)

	return fmt.Sprintf("%v", &val)

}


// [-0, +0, –]
// http://www.lua.org/manual/5.3/manual.html#lua_topointer
func (self *luaState) ToPointer(idx int) interface{} {
	return self.stack.get(idx)
}


// 把指定索引处的值转换为线程并返回
// 如果值不是线程, 就返回nil
func (self *luaState) ToThread(idx int) LuaState {
	val := self.stack.get(idx)
	if val != nil {
		if ls, ok := val.(*luaState); ok {
			return ls
		}
	}
	return nil
}